import { Request, Response, NextFunction, RequestHandler } from 'express';
import { ParamsDictionary, Query } from 'express-serve-static-core';
import { ParsedQs } from 'qs';
import { AuthenticatedRequest } from './authorization';
import { AuthUser } from '../types/auth';
import { logger } from '../utils/logger';
import { Errors } from '../types/errors';

// === Base Types ===
export interface BaseRequest extends Request {
  user?: AuthUser;
}

// === Extended Types ===
export interface TypedRequest<
  P extends ParamsDictionary = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery extends Query = ParsedQs
> extends BaseRequest {
  params: P;
  body: ReqBody;
  query: ReqQuery;
  user: AuthUser;
}

export interface TypedResponse<ResBody = any> extends Response<ResBody> {}

export type TypedHandler<
  P extends ParamsDictionary = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery extends Query = ParsedQs
> = (
  req: TypedRequest<P, ResBody, ReqBody, ReqQuery>,
  res: TypedResponse<ResBody>,
  next: NextFunction
) => Promise<void> | void;

// === Helper Functions ===
function assertUser(req: BaseRequest): asserts req is AuthenticatedRequest {
  if (!req.user) {
    throw Errors.unauthorized('User not authenticated');
  }
}

// === Handler Wrappers ===
export function createHandler<
  P extends ParamsDictionary = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery extends Query = ParsedQs
>(
  handler: TypedHandler<P, ResBody, ReqBody, ReqQuery>
): RequestHandler {
  return async (req, res, next) => {
    try {
      // Assert authentication
      assertUser(req);

      // Log request
      logger.debug('Processing request', {
        method: req.method,
        path: req.path,
        userId: req.user._id,
        body: req.method !== 'GET' ? req.body : undefined,
        query: req.query
      });

      // Execute handler
      await handler(req as TypedRequest<P, ResBody, ReqBody, ReqQuery>, res, next);

    } catch (error) {
      next(error);
    }
  };
}

export function createProtectedHandler<
  P extends ParamsDictionary = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery extends Query = ParsedQs
>(
  handler: TypedHandler<P, ResBody, ReqBody, ReqQuery>,
  roles: string[]
): RequestHandler {
  return async (req, res, next) => {
    try {
      // Assert authentication
      assertUser(req);

      // Check role
      if (!roles.includes(req.user.role)) {
        throw Errors.forbidden('Insufficient permissions');
      }

      // Log request
      logger.debug('Processing protected request', {
        method: req.method,
        path: req.path,
        userId: req.user._id,
        userRole: req.user.role,
        requiredRoles: roles,
        body: req.method !== 'GET' ? req.body : undefined,
        query: req.query
      });

      // Execute handler
      await handler(req as TypedRequest<P, ResBody, ReqBody, ReqQuery>, res, next);

    } catch (error) {
      next(error);
    }
  };
}

// === Route Configuration Types ===
export interface RouteConfig<
  P extends ParamsDictionary = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery extends Query = ParsedQs
> {
  handler: TypedHandler<P, ResBody, ReqBody, ReqQuery>;
  roles?: string[];
  validator?: RequestHandler[];
}

// === Route Collection Helper ===
export function createRouteHandler<
  P extends ParamsDictionary = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery extends Query = ParsedQs
>(config: RouteConfig<P, ResBody, ReqBody, ReqQuery>): RequestHandler[] {
  const { handler, roles, validator = [] } = config;
  
  const wrappedHandler = roles 
    ? createProtectedHandler<P, ResBody, ReqBody, ReqQuery>(handler, roles)
    : createHandler<P, ResBody, ReqBody, ReqQuery>(handler);

  return [...validator, wrappedHandler];
}
